////////////////////////////////////////////////////////////////////////////////
// THIS IMPLEMENTATION SHOULD DEPEND ON ONLY ON STL, OpenGL, AntTweakBar and
// Trackball
////////////////////////////////////////////////////////////////////////////////
#include "Preview3D.h"
#ifndef PREVIEW3D_NO_TRACKBALL
#include "trackball.h"
#endif

#ifdef __APPLE__
#   include <OpenGL/gl.h>
#   include <OpenGL/glu.h>
#else
#   ifdef _WIN32
#       include <windows.h>
#   endif
#   include <GL/gl.h>
#   include <GL/glu.h>
#endif

#include <cmath>
#include <cstdio>
#include <string>
#include <limits>
#include <cassert>

#ifndef PREVIEW3D_NO_SHADERS
#include "directionalperpixel.h"
#include "isolines.h"
#endif

#ifdef _OPENMP 
#include <omp.h> 
#else 
#define omp_get_thread_num() 0 
#endif 

#ifdef __APPLE__
#include <sys/time.h>
#endif

#ifndef _NOMATLAB_
#include "matlabio.h"
#endif

// Max line size for reading files
#define MAX_LINE 1000

////////////////////////////////////////////////////////////////////////////////
// Useful constants
////////////////////////////////////////////////////////////////////////////////
float ZERO[] = {0.0f,0.0f,0.0f,0.0f};
float DEFAULT_QUATERNION[] = { 0.0f, 0.0f, 0.0f, 1.0f };
float GOLD_AMBIENT[] = { 51.0/255.0,43.0/255.0,33.3/255.0,1.0f };
float GOLD_DIFFUSE[] = { 255.0/255.0,228.0/255.0,58.0/255.0,1.0f };
float GOLD_SPECULAR[] = { 255.0/255.0,235.0/255.0,80.0/255.0,1.0f };
float SILVER_AMBIENT[] = { 0.2f, 0.2f, 0.2f, 1.0f };
float SILVER_DIFFUSE[] = { 1.0f, 1.0f, 1.0f, 1.0f };
float SILVER_SPECULAR[] = { 1.0f, 1.0f, 1.0f, 1.0f };
float DARK_BLUE[] = { 0.3f, 0.3f, 0.5f, 1.0f };
float BLACK[] = {0.0f,0.0f,0.0f,1.0f};
//float DEFAULT_LIGHT_DIRECTION[] = { -0.57735f, -0.57735f, -0.57735f };
float DEFAULT_LIGHT_DIRECTION[] = { -0.50f, -0.40f, -0.75f};

#define SQRT_2_OVER_2 0.707106781f
float CANONICAL_VIEW_QUATERNIONS[][4] = 
  {
    {             0,             0,             0,             1},
    {             0,             0, SQRT_2_OVER_2, SQRT_2_OVER_2},
    {             0,             0,             1,             0},
    {             0,             0, SQRT_2_OVER_2,-SQRT_2_OVER_2},

    {             0,            -1,             0,             0},
    {-SQRT_2_OVER_2, SQRT_2_OVER_2,             0,             0},
    {            -1,             0,             0,             0},
    {-SQRT_2_OVER_2,-SQRT_2_OVER_2,             0,             0},

    {          -0.5,          -0.5,          -0.5,           0.5},
    {             0,-SQRT_2_OVER_2,             0, SQRT_2_OVER_2},
    {           0.5,          -0.5,           0.5,           0.5},
    { SQRT_2_OVER_2,             0, SQRT_2_OVER_2,             0},

    { SQRT_2_OVER_2,             0,-SQRT_2_OVER_2,             0},
    {           0.5,           0.5,          -0.5,           0.5},
    {             0, SQRT_2_OVER_2,             0, SQRT_2_OVER_2},
    {          -0.5,           0.5,           0.5,           0.5},

    {             0, SQRT_2_OVER_2, SQRT_2_OVER_2,             0},
    {          -0.5,           0.5,           0.5,          -0.5},
    {-SQRT_2_OVER_2,             0,             0,-SQRT_2_OVER_2},
    {          -0.5,          -0.5,          -0.5,          -0.5},

    {-SQRT_2_OVER_2,             0,             0, SQRT_2_OVER_2},
    {          -0.5,          -0.5,           0.5,           0.5},
    {             0,-SQRT_2_OVER_2, SQRT_2_OVER_2,             0},
    {           0.5,          -0.5,           0.5,          -0.5}
  };
#define NUM_CANONICAL_VIEW_QUATERNIONS 24

Preview3D::Preview3D(int start_time, bool deleteonexit)
{
  // Initialize fields
  number_of_vertices = 0;
  number_of_faces = 0;
  delete_on_exit = deleteonexit;
  // Causes gldeletelists to return error on first call
  faces_display_list = -1;
  frustum_shift_x = 0.125;
  g_Zoom = 1.0f;
  zoom = 1.0f;
  Preview3D::CopyArray3(ZERO,shift);
  Preview3D::CopyArray4(DEFAULT_QUATERNION,g_Rotation);
  trackball_snap_to_canonical = 0.0;
  g_AutoRotate = 0;
  auto_rotate_restart = false;
  g_RotateTime = 0;
  Preview3D::CopyArray4(DEFAULT_QUATERNION,g_RotateStart);
  Preview3D::CopyArray4(GOLD_AMBIENT,g_MatAmbient);
  Preview3D::CopyArray4(GOLD_DIFFUSE,g_MatDiffuse);
  Preview3D::CopyArray4(GOLD_SPECULAR,g_MatSpecular);
  g_MatShininess = 35.0f;
  Preview3D::CopyArray4(DARK_BLUE,background_color);
  Preview3D::CopyArray4(BLACK,line_color);
  g_LightMultiplier = 1.0f;
  Preview3D::CopyArray3(DEFAULT_LIGHT_DIRECTION,g_LightDirection);
  flat_shading = true;
  is_compiled = false;
  show_faces = true;
  show_lines = true;
  invert_normals = false;
#ifndef PREVIEW3D_NO_SHADERS
  // Load shaders
  s_directionalPerPixelProgram = loadShaderProgramStr(directionalperpixel_vert, directionalperpixel_frag);
  s_isolinesProgram = loadShaderProgramStr(isolines_vert, isolines_frag);
  shader_mode = DIRECTIONAL_PER_PIXEL;
#endif
	
  // initialize scroll position to 0
  scroll_position = 0.0f;


#ifndef PREVIEW3D_NO_ANTTWEAKBAR
  TwBar *bar; // Pointer to the tweak bar
  float axis[] = { 0.7f, 0.7f, 0.0f }; // initial model rotation
  float angle = 0.8f;
  
  // Initialize AntTweakBar
  if( !TwInit(TW_OPENGL, NULL) )
  {
    // A fatal error occured    
    fprintf(stderr, "AntTweakBar initialization failed: %s\n", TwGetLastError());
  }

  // Create a tweak bar
  bar = TwNewBar("TweakBar");
  TwDefine(" GLOBAL help='This is a simple 3D mesh previewer.' "); // Message added to the help bar.
  TwDefine(" TweakBar size='200 550' color='76 76 127' "); // change default tweak bar size and color
  
  TwAddVarRO(bar,"NumberOfVertices",TW_TYPE_UINT32, &number_of_vertices,
      " label='Number of vertices' help='Displays number of vertices in mesh.'"
      " group='Mesh statistics'");
  TwAddVarRO(bar,"NumberOfFaces",TW_TYPE_UINT32, &number_of_faces,
      " label='Number of faces' help='Displays number of facs in mesh.'"
      " group='Mesh statistics'");
#ifdef __APPLE__
  TwAddVarRO(bar,"fps",TW_TYPE_DOUBLE, &fps,
      " label='Frames per second' help='Displays current number of frames"
      " drawn per second.'"
      " group='Mesh statistics'");
#endif
  // Add 'g_Zoom' to 'bar': this is a modifable (RW) variable of type TW_TYPE_FLOAT. Its key shortcuts are [z] and [Z].
  TwAddVarRW(bar, "Zoom", TW_TYPE_FLOAT, &g_Zoom, 
      " min=0.5 max=5 step=0.1 keyIncr=+ keyDecr=- help='Scale the object (1=original size).' ");
  // Add 'g_Rotation' to 'bar': this is a variable of type TW_TYPE_QUAT4F which defines the object's orientation
  TwAddVarRW(bar, "ObjRotation", TW_TYPE_QUAT4F, &g_Rotation, 
      " group='Scene'"
      " label='Object rotation' open help='Change the object orientation.' ");
  // Add callback to toggle auto-rotate mode (callback functions are defined above).
  TwAddVarCB(bar, "AutoRotate", TW_TYPE_BOOLCPP, SetAutoRotateCB, GetAutoRotateCB,this,
             " label='Auto-rotate' key=space help='Toggle auto-rotate mode.' ");
  TwAddButton(bar,"ViewXYPlane", view_xy_planeCB, this,
      " group='Scene'"
      " label='View XY Plane' key=z help='View the XY plane.'");
  TwAddButton(bar,"ViewXZPlane", view_xz_planeCB, this,
      " group='Scene'"
      " label='View XZ Plane' key=y help='View the XZ plane.'");
  TwAddButton(bar,"ViewYZPlane", view_yz_planeCB, this,
      " group='Scene'"
      " label='View YZ Plane' key=x help='View the XY plane.'");
  TwAddButton(bar,"SnapView", snap_to_canonical_quaternionCB, this,
      " group='Scene'"
      " label='Snap to canonical view' key=s "
      " help='Snaps view to nearest canonical view.'");
  TwAddVarRW(bar,"TrackballSnapView", TW_TYPE_DOUBLE,
      &trackball_snap_to_canonical,
      " group='Scene'"
      " label='Trackball snap' keyIncr=D keyDecr=S step=0.05 min=0.0 max=1.0"
      " help='Snaps view to nearest canonical view while using trackball. "
      " 0.0 means no snapping, 1 mean complete snapping'");
  // Add 'g_LightMultiplier' to 'bar': this is a variable of type TW_TYPE_FLOAT. Its key shortcuts are [+] and [-].
  TwAddVarRW(bar, "Multiplier", TW_TYPE_FLOAT, &g_LightMultiplier, 
      " group='Scene'"
      " label='Light booster' min=0.1 max=4 step=0.02 keyIncr='+' keyDecr='-' help='Increase/decrease the light power.' ");
  // Add 'g_LightDirection' to 'bar': this is a variable of type TW_TYPE_DIR3F which defines the light direction
  TwAddVarRW(bar, "LightDir", TW_TYPE_DIR3F, &g_LightDirection, 
      " group='Scene'"
      " label='Light direction' open help='Change the light direction.' ");
#ifndef PREVIEW3D_NO_SHADERS
  TwEnumVal ShaderModeEV[NUM_SHADER_MODE] = {
    {OFF,"OFF"},
    {DIRECTIONAL_PER_PIXEL, "Per pixel lighting"}
    //{ISOLINES,"UV lines"}
  };
  TwType ShaderModeTW = TwDefineEnum("ShaderMode", ShaderModeEV, NUM_SHADER_MODE);
  TwAddVarRW(bar, "Shader",ShaderModeTW, &shader_mode,
      " group='Scene' help='Select shader in use' keyIncr='>' keyDecr='<'");
#endif
  TwAddVarRW(bar, "ShowLines", TW_TYPE_BOOLCPP, &show_lines,  
      " group='Draw options'"
      " label='Show lines' key=l help='Toggle wire frame.'");
  TwAddVarRW(bar, "ShowFaces", TW_TYPE_BOOLCPP, &show_faces,  
             " group='Draw options'"
             " label='Show faces' key=t help='Toggle face ng.'");
  TwAddVarRW(bar, "FlatShading", TW_TYPE_BOOLCPP, &flat_shading,  
             " group='Draw options'"
             " label='Flat shading' key=T help='Toggle per face shading or per"
             " vertex shading.' ");
  TwAddVarRW(bar, "InvertNormals", TW_TYPE_BOOLCPP, &invert_normals,  
             " group='Draw options'"
             " label='Invert normals' key=i help='Invert normal directions for inside out meshes.' ");
  TwAddVarRW(bar, "Background color", TW_TYPE_COLOR3F,
    &background_color,
    " help='Select a background color' colormode=hls");
  TwAddVarRW(bar, "LineColor", TW_TYPE_COLOR3F,
    &line_color,
    " label='Line color' help='Select a outline color' ");
  // Add 'g_MatAmbient' to 'bar': this is a variable of type TW_TYPE_COLOR4F (4 floats color)
  // and is inserted into a group named 'Material'.
  TwAddVarRW(bar, "Ambient", TW_TYPE_COLOR4F, &g_MatAmbient, " group='Material' ");
  // Add 'g_MatDiffuse' to 'bar': this is a variable of type TW_TYPE_COLOR4F (4 floats color)
  // and is inserted into group 'Material'.
  TwAddVarRW(bar, "Diffuse", TW_TYPE_COLOR4F, &g_MatDiffuse, " group='Material' ");
  TwAddVarRW(bar, "Specular", TW_TYPE_COLOR4F, &g_MatSpecular, " group='Material' ");
  TwAddVarRW(bar,"Shininess",TW_TYPE_FLOAT,&g_MatShininess," group='Material'"
      " min=0 max=128");

  g_RotateTime = start_time;

  // Init rotation
  SetQuaternionFromAxisAngle(axis, angle, g_Rotation);
  SetQuaternionFromAxisAngle(axis, angle, g_RotateStart);
#endif
  
}

Preview3D::~Preview3D()
{
}

bool Preview3D::load_mesh_from_file(const char* mesh_file_name)
{
  std::string mesh_file_name_string = std::string(mesh_file_name);
  int last_dot = mesh_file_name_string.rfind('.');
  if(last_dot == std::string::npos)
  {
    // No file type determined
    printf("Error: No file extension found in %s\n",mesh_file_name);
    return false;
  }
  std::string extension = mesh_file_name_string.substr(last_dot+1);
  if(extension == "off" || extension =="OFF")
  {
    if(!read_off(mesh_file_name,vertices,faces))
    {
      vertices.clear();
      faces.clear();
      return false;
    }
  }else if(extension == "obj" || extension =="OBJ")
  {
    if(!read_obj(mesh_file_name,vertices,faces))
    {
      vertices.clear();
      faces.clear();
      return false;
    }
  }else if(extension == "wrl" || extension =="WRL")
  {
    if(!read_wrl(mesh_file_name,vertices,faces))
    {
      vertices.clear();
      faces.clear();
      return false;
    }
  }else if(extension == "mesh" || extension =="MESH")
  {
    // dummy tetrahedra list, we will just throw it away (for now)
    std::vector<std::vector<size_t > > dummy_tetrahedra;
    if(!read_mesh(mesh_file_name,vertices,faces,dummy_tetrahedra))
    {
      vertices.clear();
      faces.clear();
      return false;
    }
  }
  else if (extension == "mat" || extension =="MAT" || extension == "mp" || extension =="MP")
  {
	  MatlabIO mio(mesh_file_name);
	  if (mio.error)
	  {
		  vertices.clear();
		  faces.clear();
		  return false;
	  } else
	  {
		  vertices = mio.V;
		  faces = mio.F;
		  
		  vertex_colors = mio.VC;
		  face_colors = mio.FC;

		  if (delete_on_exit)
			  remove(mesh_file_name);
	  }
  }else
  {
    // unrecognized file type
    printf("Error: %s is not a recognized file type.\n",extension.c_str());
    return false;
  }

  printf("%d face%s\n",faces.size(),(faces.size() == 1 ? "" : "s"));
  printf("%d vert%s\n",vertices.size(),(vertices.size() == 1 ? "ex" : "ices"));
  number_of_vertices = vertices.size();
  number_of_faces = faces.size();

  if(number_of_vertices > 3000)
  {
    show_lines = false;
    flat_shading = false;
    show_faces = true;
  }else
  {
    show_lines = true;
    flat_shading = true;
    show_faces = true;
  }
  is_compiled = false;
  get_scale_and_shift_to_fit_mesh(vertices,zoom,shift);
  compute_face_normals(vertices,faces,face_normals);
  compute_vertex_normals(vertices,faces,vertex_normals);
  invert_normals = test_for_inverted_normals(vertices,vertex_normals);
  return true;
}
bool Preview3D::save_mesh_to_file(const char* mesh_file_name)
{
  std::string mesh_file_name_string(mesh_file_name);
  int last_dot = mesh_file_name_string.rfind('.');
  if(last_dot == std::string::npos)
  {
    // No file type determined
    printf("Error: No file extension found in %s\n",mesh_file_name);
    return false;
  }
  std::string extension = mesh_file_name_string.substr(last_dot+1);
  if(extension == "off" || extension =="OFF")
  {
    return write_off(mesh_file_name,vertices,faces);
  }else if(extension == "obj" || extension =="OBJ")
  {
    return write_obj(mesh_file_name,vertices,faces);
  }else if(extension == "mesh" || extension =="MESH")
  {
    //not supported yet
  }else
  {
    // unrecognized file type
    printf("Error: %s is not a recognized file type.\n",extension.c_str());
    return false;
  }
  return true;
}
bool Preview3D::key_down(unsigned char key, int mouse_x, int mouse_y)
{
  return false;
}

bool Preview3D::key_up(unsigned char key, int mouse_x, int mouse_y)
{
  return false;
}

bool Preview3D::mouse_down(
  int mouse_x, 
  int mouse_y)
{
#ifndef PREVIEW3D_NO_ANTTWEAKBAR
  TwMouseMotion(mouse_x,mouse_y);

  // First pass to AntTweakBar
  if( !TwMouseButton(TW_MOUSE_PRESSED, TW_MOUSE_LEFT) )
  {
    TwMouseButton(TW_MOUSE_RELEASED, TW_MOUSE_LEFT);
#endif
    //init track ball
    down_x = mouse_x;
    down_y = mouse_y;
    down_rotation[0] = g_Rotation[0];
    down_rotation[1] = g_Rotation[1];
    down_rotation[2] = g_Rotation[2];
    down_rotation[3] = g_Rotation[3];
    down = true;
#ifndef PREVIEW3D_NO_ANTTWEAKBAR
  }
#endif
  return true;
}

bool Preview3D::mouse_up(
  int mouse_x, 
  int mouse_y)
{
  down = false;
#ifndef PREVIEW3D_NO_ANTTWEAKBAR
  // First pass to AntTweakBar
  if( !TwMouseButton(TW_MOUSE_RELEASED, TW_MOUSE_LEFT) )
  {
  }
#endif
  return true;
}

bool Preview3D::mouse_move(int mouse_x, int mouse_y)
{
  if(down)
  {

#ifndef PREVIEW3D_NO_TRACKBALL
    glPushMatrix();
    glScaled(g_Zoom, g_Zoom, g_Zoom);
    glScaled(zoom, zoom, zoom);
    //glTranslatef(shift[0],shift[1],shift[2]);
    
    double model_view_matrix[16];
    double projection_matrix[16];
    int    view_port[4];
    glGetDoublev(GL_MODELVIEW_MATRIX,  model_view_matrix);
    glGetDoublev(GL_PROJECTION_MATRIX, projection_matrix);
    glGetIntegerv(GL_VIEWPORT, view_port);
    double origin_x, origin_y, origin_z;
    gluProject(
      0.,0.,0.,
      model_view_matrix, projection_matrix,
      view_port, &origin_x, &origin_y, &origin_z);

    float new_quaternion[4];
    // Trackball centered at center of screen
    //float center_x = ((float)(view_port[0] + view_port[2]))/2.f;
    //float center_y = ((float)(view_port[1] + view_port[3]))/2.f;
    // Trackball centered at object's centroid
    float center_x = origin_x;
    float center_y = origin_y;
    float half_width =  ((float)(view_port[2]))/4.f;
    float half_height = ((float)(view_port[3]))/4.f;
    trackball(new_quaternion,
        (center_x-down_x)/half_width,
        (down_y-center_y)/half_height,
        (center_x-mouse_x)/half_width,
        (mouse_y-center_y)/half_height);
    // I think we need to do this because we have z pointing out of the
    // screen rather than into the screen
    new_quaternion[2] = -new_quaternion[2];
    add_quats(down_rotation,new_quaternion,g_Rotation);
    Preview3D::snap_to_canonical_quaternion(
      g_Rotation,trackball_snap_to_canonical);
    glPopMatrix();
#endif 
  }
#ifndef PREVIEW3D_NO_ANTTWEAKBAR
  else if(!TwMouseMotion(mouse_x,mouse_y))
  {
  }
#endif
  return true;
}

bool Preview3D::mouse_scroll(int mouse_x, int mouse_y, float delta_y)
{
  scroll_position += delta_y;
#ifndef PREVIEW3D_NO_ANTTWEAKBAR
  bool in_bar = TwMouseMotion(mouse_x,mouse_y);
  if(!TwMouseWheel(scroll_position) && !in_bar)
  {
#endif
    double factor = 0.05f;
    g_Zoom = (g_Zoom + delta_y*factor > 0.1f ? g_Zoom + delta_y*factor : 0.1f);
    return true;
#ifndef PREVIEW3D_NO_ANTTWEAKBAR
  }
#endif
  return true;
}

void Preview3D::gather_modifiers(
    int glutButton, 
    int modifiers, 
    bool & shift_down,
    bool & control_down, 
    bool & option_down)
{
}


void Preview3D::draw(int current_time)
{

  // will be used to set light paramters
  float v[4]; 
  // Clear frame buffer
  glClearColor(
      background_color[0],
      background_color[1],
      background_color[2],
      background_color[3]);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glEnable(GL_DEPTH_TEST);
  glDisable(GL_CULL_FACE);
  glEnable(GL_NORMALIZE);

  // Set light
  glEnable(GL_LIGHTING);
  glEnable(GL_LIGHT0);
  v[0] = v[1] = v[2] = g_LightMultiplier*0.4f; v[3] = 1.0f;
  glLightfv(GL_LIGHT0, GL_AMBIENT, v);
  v[0] = v[1] = v[2] = g_LightMultiplier*0.8f; v[3] = 1.0f;
  glLightfv(GL_LIGHT0, GL_DIFFUSE, v);
  v[0] = -g_LightDirection[0]; v[1] = -g_LightDirection[1]; v[2] = -g_LightDirection[2]; v[3] = 0.0f;
  glLightfv(GL_LIGHT0, GL_POSITION, v);

#ifndef PREVIEW3D_NO_SHADERS
  switch(shader_mode){
    case DIRECTIONAL_PER_PIXEL:
      {
        glUseProgram(s_directionalPerPixelProgram.p);
        break;
      }
    //case ISOLINES:
    //  {
    //    glUseProgram(s_isolinesProgram.p);
    //    break;
    //  }
    default:{}
  }
#endif

  //Preview3D::draw_floor();

  push_scene(current_time);

  // Set material
  glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, g_MatAmbient);
  glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, g_MatDiffuse);
  glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, g_MatSpecular);
  glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS,g_MatShininess);
  
  if (vertex_colors.size() > 0 || face_colors.size() > 0)
  {
    glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE);	
    glEnable(GL_COLOR_MATERIAL);
  }
  
#ifdef __APPLE__
  timeval time;
  gettimeofday(&time, NULL);
  double start_seconds = time.tv_sec + time.tv_usec / 1e6;
#endif

  // need to recompile if flat_shading or invert_normals has changed
  is_compiled = is_compiled &&
    (flat_shading == flat_shading_at_compile) &&
    (invert_normals == invert_normals_at_compile);

  if(!is_compiled)
  {
    Preview3D::compile_mesh(
      vertices,
      faces,
      vertex_normals,
	  vertex_colors,
      face_normals,
	  face_colors,
      flat_shading_at_compile = flat_shading,
      invert_normals_at_compile = invert_normals,
      faces_display_list);
    is_compiled = true;
  }

  Preview3D::draw_mesh(
    vertices,
    faces,
    vertex_normals,
    vertex_colors,
    face_normals,
	face_colors,
    show_lines,
    line_color,
    show_faces,
    faces_display_list);

  glDisable(GL_COLOR_MATERIAL);

#ifdef __APPLE__
  gettimeofday(&time, NULL);
  double end_seconds = time.tv_sec + time.tv_usec / 1e6;
  fps = 1.0/(end_seconds-start_seconds);
#endif

  pop_scene();
  glUseProgram(0);

  // Draw tweak bars
#ifndef PREVIEW3D_NO_ANTTWEAKBAR
  TwDraw();
#endif
}


void Preview3D::push_scene(int current_time)
{
  float mat[4*4]; // rotation matrix
  // Rotate scene
  glPushMatrix();
  // not sure why this was here
  //glTranslatef(0.5f, -0.3f, 0.0f);
  if( g_AutoRotate ) 
  {
    if(auto_rotate_restart)
    {
      g_RotateStart[0] = g_Rotation[0];
      g_RotateStart[1] = g_Rotation[1];
      g_RotateStart[2] = g_Rotation[2];
      g_RotateStart[3] = g_Rotation[3];
      g_RotateTime = current_time;
      auto_rotate_restart = false;
    }
      float axis[3] = { 0, 1, 0 };
      float angle = (float)(current_time-g_RotateTime)/1000.0f;
      float quat[4];
      Preview3D::SetQuaternionFromAxisAngle(axis, angle, quat);
      Preview3D::MultiplyQuaternions(g_RotateStart, quat, g_Rotation);
  }
  Preview3D::ConvertQuaternionToMatrix(g_Rotation, mat);
  glMultMatrixf(mat);
  glScaled(g_Zoom, g_Zoom, g_Zoom);
  glScaled(zoom, zoom, zoom);
  glTranslatef(shift[0],shift[1],shift[2]);
}

void Preview3D::pop_scene()
{
  glPopMatrix();
}

void Preview3D::resize(int width, int height)
{
  this->width = width;
  this->height = height;
  // Set OpenGL viewport and camera
  glViewport(0, 0, width, height);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  //gluPerspective(40, (double)width/height, 1, 100);
  // from: http://nehe.gamedev.net/data/articles/article.asp?article=11
  const GLdouble pi = 3.1415926535897932384626433832795;
  double angle = 45.0;
  double near = 1.0;
  double far = 100.0;
  double fH = tan( angle / 360.0 * pi ) * near;
  double fW = fH * (double)width/(double)height;
  // shift everything a little to the right
  glFrustum( -fW-frustum_shift_x, fW-frustum_shift_x, -fH, fH,near, far);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  gluLookAt(0.0,0,5, 0,0,0, 0,1,0);
  // shift everything a little to the right
  //glTranslatef(0.5f, 0.0f, 0.0f);
  
#ifndef PREVIEW3D_NO_ANTTWEAKBAR
  // Send the new window size to AntTweakBar
  TwWindowSize(width, height);
#endif
}


void Preview3D::SetAutoRotate(int value)
{
  
#ifndef PREVIEW3D_NO_ANTTWEAKBAR
  g_AutoRotate = value; // copy value to g_AutoRotate
  if( g_AutoRotate!=0 ) 
  {
    // init rotation
    //g_RotateTime = glutGet(GLUT_ELAPSED_TIME);
    auto_rotate_restart = true;
    
    // make Rotation variable read-only
    TwDefine(" TweakBar/ObjRotation readonly ");
  }
  else
    // make Rotation variable read-write
    TwDefine(" TweakBar/ObjRotation readwrite ");
#endif
}

int Preview3D::GetAutoRotate()
{
  return g_AutoRotate; // copy g_AutoRotate to value
}

void Preview3D::view_xy_plane()
{
  g_Rotation[0] = 0.0f;
  g_Rotation[1] = 0.0f;
  g_Rotation[2] = 0.0f;
  g_Rotation[3] = 1.0f;
  auto_rotate_restart = g_AutoRotate;
}

void Preview3D::view_xz_plane()
{
  g_Rotation[0] = -sqrt(2.0f)/2.0f;
  g_Rotation[1] = 0.0f;
  g_Rotation[2] = 0.0f;
  g_Rotation[3] = sqrt(2.0f)/2.0f;
  auto_rotate_restart = g_AutoRotate;
}

void Preview3D::view_yz_plane()
{
  g_Rotation[0] = -0.5f;
  g_Rotation[1] = -0.5f;
  g_Rotation[2] = -0.5f;
  g_Rotation[3] = 0.5f;
  auto_rotate_restart = g_AutoRotate;
}

bool Preview3D::snap_to_canonical_quaternion(
  float q[4],
  const double threshold)
{
  // 0.290019
  // 0.300000
  const double MAX_DISTANCE = 0.4;
  double min_distance = 2*MAX_DISTANCE;
  int min_index = -1;
  int min_sign = 0;
  // loop over canonical view quaternions
  for(int sign = -1;sign<=1;sign+=2)
  {
    for(int i = 0; i<NUM_CANONICAL_VIEW_QUATERNIONS; i++)
    {
      float distance = 0.0;
      // loop over coordinates
      for(int j = 0;j<4;j++)
      {
        distance += 
          (q[j]-sign*CANONICAL_VIEW_QUATERNIONS[i][j])*
          (q[j]-sign*CANONICAL_VIEW_QUATERNIONS[i][j]);
      }
      if(min_distance > distance)
      {
        min_distance = distance;
        min_index = i;
        min_sign = sign;
      }
    }
  }

  if(MAX_DISTANCE < min_distance)
  {
    printf("FOUND NEW MAX MIN_DISTANCE: %g\n",min_distance);
  }

  assert(min_distance < MAX_DISTANCE);
  assert(min_index >= 0);

  //printf("min/max: %g <=? %g\n",(min_distance/MAX_DISTANCE),threshold);
  if( min_distance/MAX_DISTANCE <= threshold)
  {
    // loop over coordinates
    for(int j = 0;j<4;j++)
    {
      q[j] = min_sign*CANONICAL_VIEW_QUATERNIONS[min_index][j];
    }
    return true;
  }
  return false;
}

#ifndef _NOMATLAB_
bool Preview3D::read_mat(const char* mat_file_name, MatlabIO& mio)
{
	mio = MatlabIO(mat_file_name);
}

#endif


bool Preview3D::read_obj(
  const char* obj_file_name,
  std::vector<Vector > & vertices, 
  std::vector<std::vector<size_t> > & faces)
{
  FILE * obj_file = fopen(obj_file_name,"r");                                       
  if(NULL==obj_file)
  {
    printf("IOError: %s could not be opened...",obj_file_name);
    return false; 
  }

  vertices.clear();
  faces.clear();
  bool has_texture = false;
  bool has_normals = false;
  char type[2];
  std::string v("v");
  std::string vn("vn");
  std::string vt("vt");
  std::string f("f");
  std::string tic_tac_toe("#");
  while(fscanf(obj_file, "%s",type)==1)
  {
    if(type == v)
    {
      float x,y,z;
      fscanf(obj_file, "%g %g %g\n",&x,&y,&z);
      Vector vertex;
      vertex.resize(3);
      vertex[0] = x;
      vertex[1] = y;
      vertex[2] = z;
      vertices.push_back(vertex);
    }else if(type == vn)
    {
      has_normals = true;
      float x,y,z;
      fscanf(obj_file, "%g %g %g\n",&x,&y,&z);      
    }else if(type == vt)
    {
      has_texture = true;
      float x,y,z;
      fscanf(obj_file, "%g %g %g\n",&x,&y,&z);      
    }else if(type == f)
    {
      
      std::vector<size_t> face;
      size_t i,it,in;
      if(has_texture & has_normals)
      {
        while(fscanf(obj_file,"%u/%u/%u",&i,&it,&in)==3)
        {
          face.push_back(i-1);
        }
      }
      else if(has_texture)
      {
        while(fscanf(obj_file,"%u/%u",&i,&it)==2)
        {
          face.push_back(i-1);
        }
      }
      else if(has_normals)
      {
        while(fscanf(obj_file,"%u//%u",&i,&in)==2)
        {
          face.push_back(i-1);
        }
      }
      else
      {
        while(fscanf(obj_file,"%u",&i)==1)
        {
          face.push_back(i-1);
        }
      }
      
      faces.push_back(face);
    }else if(type == tic_tac_toe)
    {
      //ignore comments
      char comment[1000];
      fscanf(obj_file,"%[^\n]",comment);
    }else
    {
      //ignore any other lines
      char comment[1000];
      fscanf(obj_file,"%[^\n]",comment);
    }
  }
//  if(max_face_vertex > vertices.size())
//  {
//    fprintf(stderr,"ERROR: Face indices exceed number of vertices\n");
//    vertices.clear();
//    faces.clear();
//    fclose(obj_file);
//    return false;
//  }
  fclose(obj_file);
  return true;
}

bool Preview3D::read_off(
  const char* off_file_name,
  std::vector<Vector > & vertices, 
  std::vector<std::vector<size_t> > & faces)
{
  FILE * off_file = fopen(off_file_name,"r");                                       
  if(NULL==off_file)
  {
    printf("IOError: %s could not be opened...",off_file_name);
    return false; 
  }
  vertices.clear();
  faces.clear();
  // First line is always OFF
  char header[3];
  const std::string OFF("OFF");
  const std::string NOFF("NOFF");
  if(!fscanf(off_file,"%s\n",&header)==1
      || !(OFF == header || NOFF == header))
  {
    printf("Error: %s's first line should be OFF or NOFF not %s...",off_file_name,header);
    fclose(off_file);
    return false; 
  }
  bool has_normals = NOFF==header;
  // Second line is #vertices #faces #edges
  int number_of_vertices;
  int number_of_faces;
  int number_of_edges;
  char tic_tac_toe;
  char line[1000];
  bool still_comments = true;
  while(still_comments)
  {
    fgets(line,1000,off_file);
    still_comments = line[0] == '#';
  }
  sscanf(line,"%d %d %d",&number_of_vertices,&number_of_faces,&number_of_edges);
  vertices.resize(number_of_vertices);
  faces.resize(number_of_faces);
  //printf("%s %d %d %d\n",(has_normals ? "NOFF" : "OFF"),number_of_vertices,number_of_faces,number_of_edges);
  // Read vertices
  for(int i = 0;i<number_of_vertices;)
  {
    float x,y,z,nx,ny,nz;
    if((has_normals && fscanf(off_file, "%g %g %g %g %g %g\n",&x,&y,&z,&nx,&ny,&nz)==6) || 
      (!has_normals && fscanf(off_file, "%g %g %g\n",&x,&y,&z)==3))
    {
      Vector vertex;
      vertex.resize(3);
      vertex[0] = x;
      vertex[1] = y;
      vertex[2] = z;
      vertices[i] = vertex;
      i++;
    }else if(
      fscanf(off_file,"%[#]",&tic_tac_toe)==1)
    {
      char comment[1000];
      fscanf(off_file,"%[^\n]",comment);
    }else
    {
      printf("Error: bad line in %s\n",off_file_name);
      fclose(off_file);
      return false;
    }
  }
  // Read faces
  for(int i = 0;i<number_of_faces;)
  {
    std::vector<size_t> face;
    int valence;
    if(fscanf(off_file,"%d",&valence)==1)
    {
      face.resize(valence);
      for(int j = 0;j<valence;j++)
      {
        int index;
        if(j<valence-1)
        {
          fscanf(off_file,"%d",&index);
        }else{
          fscanf(off_file,"%d%*[^\n]",&index);
        }

        face[j] = index;
      }
      faces[i] = face;
      i++;
    }else if(
      fscanf(off_file,"%[#]",&tic_tac_toe)==1)
    {
      char comment[1000];
      fscanf(off_file,"%[^\n]",comment);
    }else
    {
      printf("Error: bad line in %s\n",off_file_name);
      fclose(off_file);
      return false;
    }
  }
  fclose(off_file);
  return true;
}

bool Preview3D::read_mesh(
  const char* mesh_file_name,
  std::vector<Vector > & vertices, 
  std::vector<std::vector<size_t> > & triangles,
  std::vector<std::vector<size_t> > & tetrahedra)
{
  FILE * mesh_file = fopen(mesh_file_name,"r");                                       
  if(NULL==mesh_file)
  {
    printf("IOError: %s could not be opened...",mesh_file_name);
    return false; 
  }
  char line[MAX_LINE];
  bool still_comments;

  still_comments= true;
  while(still_comments)
  {
    fgets(line,MAX_LINE,mesh_file);
    still_comments = (line[0] == '#' || line[0] == '\n');
  }

  char str[MAX_LINE];
  sscanf(line," %s",str);
  // check that first word is MeshVersionFormatted
  if(0!=strcmp(str,"MeshVersionFormatted"))
  {
    printf("Error: first word should be MeshVersionFormatted not %s\n",str);
    return false;
  }
  int one = -1;
  if(2 != sscanf(line,"%s %d",str,&one))
  {
    // 1 appears on next line?
    fscanf(mesh_file," %d",&one);
  }
  if(one != 1)
  {
    printf("Error: second word should be 1 not %d\n",one);
    return false;
  }

  // eat comments
  still_comments= true;
  while(still_comments)
  {
    fgets(line,MAX_LINE,mesh_file);
    still_comments = (line[0] == '#' || line[0] == '\n');
  }

  sscanf(line," %s",str);
  // check that third word is Dimension
  if(0!=strcmp(str,"Dimension"))
  {
    printf("Error: third word should be Dimension not %s\n",str);
    return false;
  }
  int three = -1;
  if(2 != sscanf(line,"%s %d",str,&three))
  {
    // 1 appears on next line?
    fscanf(mesh_file," %d",&three);
  }
  if(three != 3)
  {
    printf("Error: only Dimension 3 supported not %d\n",three);
    return false;
  }


  // eat comments
  still_comments= true;
  while(still_comments)
  {
    fgets(line,MAX_LINE,mesh_file);
    still_comments = (line[0] == '#' || line[0] == '\n');
  }


  sscanf(line," %s",str);
  // check that fifth word is Vertices
  if(0!=strcmp(str,"Vertices"))
  {
    printf("Error: fifth word should be Vertices not %s\n",str);
    return false;
  }
  size_t number_of_vertices;
  if(1 != fscanf(mesh_file," %u",&number_of_vertices))
  {
    printf("Error: expecting number of vertices...\n");
    return false;
  }
  // allocate space for vertices
  Vector point;
  point.resize(3,0.0);
  vertices.resize(number_of_vertices,point);
  size_t extra;
  double x,y,z;
  for(size_t i = 0;i<number_of_vertices;i++)
  {
    if(4 != fscanf(mesh_file," %lf %lf %lf %u",&x,&y,&z,&extra))
    {
      printf("Error: expecting vertex position...\n");
      return false;
    }
    vertices[i][0] = x;
    vertices[i][1] = y;
    vertices[i][2] = z;
  }


  // eat comments
  still_comments= true;
  while(still_comments)
  {
    fgets(line,MAX_LINE,mesh_file);
    still_comments = (line[0] == '#' || line[0] == '\n');
  }


  sscanf(line," %s",str);
  // check that sixth word is Triangles
  if(0!=strcmp(str,"Triangles"))
  {
    printf("Error: sixth word should be Triangles not %s\n",str);
    return false;
  }
  size_t number_of_triangles;
  if(1 != fscanf(mesh_file," %u",&number_of_triangles))
  {
    printf("Error: expecting number of triangles...\n");
    return false;
  }
  // allocate space for vertices
  std::vector<size_t> triangle;
  triangle.resize(3,0);
  triangles.resize(number_of_triangles,triangle);
  size_t a,b,c;
  for(size_t i = 0;i<number_of_triangles;i++)
  {
    if(4 != fscanf(mesh_file," %u %u %u %u",&a,&b,&c,&extra))
    {
      printf("Error: expecting triangle indices...\n");
      return false;
    }
    triangles[i][0] = a-1;
    triangles[i][1] = b-1;
    triangles[i][2] = c-1;
  }


  // eat comments
  still_comments= true;
  while(still_comments)
  {
    fgets(line,MAX_LINE,mesh_file);
    still_comments = (line[0] == '#' || line[0] == '\n');
  }


  sscanf(line," %s",str);
  // check that sixth word is Triangles
  if(0!=strcmp(str,"Tetrahedra"))
  {
    printf("Error: seventh word should be Tetrahedra not %s\n",str);
    return false;
  }
  size_t number_of_tetrahedra;
  if(1 != fscanf(mesh_file," %u",&number_of_tetrahedra))
  {
    printf("Error: expecting number of tetrahedra...\n");
    return false;
  }
  // allocate space for vertices
  std::vector<size_t> tetrahedron;
  tetrahedron.resize(4,0);
  tetrahedra.resize(number_of_tetrahedra,tetrahedron);
  size_t d;
  for(size_t i = 0;i<number_of_tetrahedra;i++)
  {
    if(5 != fscanf(mesh_file," %u %u %u %u %u",&a,&b,&c,&d,&extra))
    {
      printf("Error: expecting tetrahedra indices...\n");
      return false;
    }
    tetrahedra[i][0] = a-1;
    tetrahedra[i][1] = b-1;
    tetrahedra[i][2] = c-1;
    tetrahedra[i][3] = d-1;
  }

  fclose(mesh_file);

  assert(number_of_vertices == vertices.size());
  assert(number_of_triangles == triangles.size());
  assert(number_of_tetrahedra == tetrahedra.size());
  return true;
}

bool Preview3D::read_wrl(
  const char* wrl_file_name,
  std::vector<Vector > & vertices, 
  std::vector<std::vector<size_t> > & faces)
{
  FILE * wrl_file = fopen(wrl_file_name,"r");                                       
  if(NULL==wrl_file)
  {
    printf("IOError: %s could not be opened...",wrl_file_name);
    return false; 
  }

  vertices.clear();
  faces.clear();

  char line[1000];
  // Read lines until seeing "point ["
  // treat other lines in file as "comments"
  bool still_comments = true;
  std::string needle("point [");
  std::string haystack;
  while(still_comments)
  {
    fgets(line,1000,wrl_file);
    haystack = std::string(line);
    still_comments = std::string::npos == haystack.find(needle); 
  }

  // read points in sets of 3
  int floats_read = 3;
  double x,y,z;
  while(floats_read == 3)
  {
    floats_read = fscanf(wrl_file," %lf %lf %lf,",&x,&y,&z);
    if(floats_read == 3)
    {
      Vector point;
      point.resize(3);
      point[0] = x;
      point[1] = y;
      point[2] = z;
      vertices.push_back(point);
      //printf("(%g, %g, %g)\n",x,y,z);
    }else if(floats_read != 0)
    {
      printf("ERROR: unrecognized format...\n");
      return false;
    }
  }
  // Read lines until seeing "coordIndex ["
  // treat other lines in file as "comments"
  still_comments = true;
  needle = std::string("coordIndex [");
  while(still_comments)
  {
    fgets(line,1000,wrl_file);
    haystack = std::string(line);
    still_comments = std::string::npos == haystack.find(needle); 
  }
  // read faces
  int ints_read = 1;
  while(ints_read > 0)
  {
    // read new face indices (until hit -1)
    std::vector<size_t> face;
    bool reached_minus_one = false;
    while(true)
    {
      // indices are 0-indexed
      int i;
      ints_read = fscanf(wrl_file," %d,",&i);
      if(ints_read > 0)
      {
        if(i>=0)
        {
          face.push_back(i);
        }else
        {
          faces.push_back(face);
          break;
        }
      }else
      {
        break;
      }
    }
  }
  


  fclose(wrl_file);
  return true;
}

bool Preview3D::write_off(
  const char* off_file_name,
  const std::vector<Vector > & vertices, 
  const std::vector<std::vector<size_t> > & faces)
{
  FILE * off_file = fopen(off_file_name,"w"); 
  if(NULL==off_file)
  {
    printf("IOError: %s could not be opened for writing...",off_file_name);
    return false; 
  }
  // Print header
  fprintf(off_file,"OFF\n");
  // Print number of vertices, number of faces, number of edges
  printf(
    "WARNING: "
    "Assuming watertight triangle mesh... edge count could be wrong..\n");
  fprintf(off_file,"%d %d %d\n",vertices.size(),faces.size(),3*faces.size()/2);
  // Loop over vertices
  for(size_t i = 0;i<vertices.size();i++)
  {
    fprintf(off_file,"%0.15g %0.15g %0.15g\n",
      vertices[i][0],
      vertices[i][1],
      vertices[i][2]
      );
  }
  // loop over faces
  for(size_t i = 0;i<faces.size();i++)
  {
    // print valence of face
    fprintf(off_file,"%d",faces[i].size());
    for(size_t j = 0; j<faces[i].size();j++)
    {
      // OFF is 0-indexed
      fprintf(off_file," %d",faces[i][j]);
    }
    fprintf(off_file,"\n");
  }

  fclose(off_file);
  return true;
}

bool Preview3D::write_obj(
  const char* obj_file_name,
  const std::vector<Vector > & vertices, 
  const std::vector<std::vector<size_t> > & faces)
{
  FILE * obj_file = fopen(obj_file_name,"w");
  if(NULL==obj_file)
  {
    printf("IOError: %s could not be opened for writing...",obj_file_name);
    return false;                                              
  }
  // Loop over vertices
  for(size_t i = 0;i<vertices.size();i++)
  {
    fprintf(obj_file,"v %0.15g %0.15g %0.15g\n",
      vertices[i][0],
      vertices[i][1],
      vertices[i][2]
      );
  }
  // loop over faces
  for(size_t i = 0;i<faces.size();i++)
  {
    fprintf(obj_file,"f");
    for(size_t j = 0; j<faces[i].size();j++)
    {
      // OBJ is 1-indexed
      fprintf(obj_file," %d",faces[i][j]+1);
    }
    fprintf(obj_file,"\n");
  }
  fclose(obj_file);
  return true;
}

//void loadOBJ(const char *filename, MeshBuffer *mb)
//{
//    bool has_normals=false, has_uvs=false;
//
//    double x,y,z,l;
//    Vec8 cv;
//    Vec9 tn;
//    unsigned int posidx, noridx, uvidx;
//    unsigned int n, tTag;
//
//    std::string line, token;
//
//    std::ifstream ifs(filename);
//    if (!ifs.is_open())
//    {
//        std::cerr<<"Failed to open file: "<<filename<<std::endl;
//        return;
//    }
//    std::istringstream iss;
//
//    while (getline(ifs, line))
//    {
//        iss.clear(); 
//        iss.str(line);
//
//        iss>>token;
//
//        if (token.compare("#num_v")==0)
//        {
//            iss>>n; if (!iss.fail()) mb->positions.reserve(n);
//        }
//        else if (token.compare("#num_vt")==0)
//        {
//            iss>>n; if (!iss.fail()) mb->uvs.reserve(n);
//        }
//        else if (token.compare("#num_vn")==0)
//        {
//            iss>>n; if (!iss.fail()) mb->normals.reserve(n);
//        }
//        else if (token.compare("#num_f")==0)
//        {
//            iss>>n; if (!iss.fail()) mb->idx_sizes.reserve(n);
//        }
//        else if (token.compare("#num_f_v")==0)
//        {
//            iss>>n; if (!iss.fail()) mb->idx_pos.reserve(n);
//        }
//
//        else if (token.compare("v")==0)
//        {
//            iss>>x>>y>>z; 
//            if (!iss.fail()) 
//            {
//                mb->positions.push_back( Vec3(x,y,z) );
//            }
//        }
//        else if (token.compare("vn")==0)
//        {
//            iss>>x>>y>>z; 
//            l = sqrt(x*x+y*y+z*z); if (l==0) l=1;
//            if (!iss.fail()) 
//            {
//                mb->normals.push_back( Vec3(x/l,y/l,z/l) ); 
//            }
//        }
//        else if (token.compare("vt")==0)
//        {
//            iss>>x>>y; 
//            if (!iss.fail()) 
//            {
//                mb->uvs.push_back(Vec2(x,y));
//            }
//        }
//        else if (token.compare("vC")==0) // load per-vertex principal curvature/directions
//        {
//            iss>>cv[0]>>cv[1]>>cv[2]>>cv[3]>>cv[4]>>cv[5]>>cv[6]>>cv[7];
//            if (!iss.fail()) 
//            {
//                mb->curvatures.push_back( cv );
//            }
//        }
//        else if (token.compare("T")==0) // T-joint tags for last face
//        {
//            unsigned int tagFaceStart = mb->tTags.size();
//            mb->tTags.push_back(mb->idx_sizes.size()-1);
//
//            while (!iss.eof())
//            {
//                iss>>token; if (iss.fail()) break;
//
//                if (sscanf(token.c_str(),"%d",&tTag) == 1)
//                {
//                    mb->tTags.push_back(tTag);
//                }
//                else
//                {
//                    mb->tTags.erase(mb->tTags.begin()+tagFaceStart, mb->tTags.end());
//                    break;
//                }
//            }
//
//        }
//        else if (token.compare("f")==0)
//        {
//            mb->idx_sizes.push_back( 0 );
//
//            while (!iss.eof())
//            {
//                bool valid_entry = true;
//
//                iss>>token; if (iss.fail()) break;
//                if (sscanf(token.c_str(), "%d/%d/%d", &posidx, &uvidx, &noridx) == 3)
//                {
//                    if (!has_uvs) mb->idx_uv.reserve(mb->idx_pos.capacity());
//                    if (!has_normals) mb->idx_nor.reserve(mb->idx_pos.capacity());
//
//                    has_uvs = has_normals = true;
//                }
//                else if (sscanf(token.c_str(),"%d//%d",&posidx, &noridx) == 2)
//                {
//                    if (!has_normals) mb->idx_nor.reserve(mb->idx_pos.capacity());
//
//                    has_normals = true;
//                }
//                else if (sscanf(token.c_str(),"%d/%d",&posidx, &uvidx) == 2)
//                {
//                    if (!has_uvs) mb->idx_uv.reserve(mb->idx_pos.capacity());
//
//                    has_uvs = true;
//                }
//                else if (sscanf(token.c_str(),"%d/",&posidx) == 1)
//                {
//                    // nothing to change.
//                }
//                else
//                {
//                    valid_entry = false;
//
//                }
//                if (valid_entry)
//                {
//                    posidx--; noridx--; uvidx--;
//                    mb->idx_sizes.back()++;
//                    mb->idx_pos.push_back(posidx);
//                    if (has_normals) mb->idx_nor.push_back(noridx);
//                    if (has_uvs)     mb->idx_uv.push_back(uvidx);
//                }
//            }
//        } 
//    }
//
//    ifs.close();
//
//    if (!has_normals) mb->idx_nor.clear();
//    if (!has_uvs) mb->idx_uv.clear();
//
//    cout<<"Loaded "<<filename<<endl;
//
//    printMeshBufferStats(mb);
//
//}

void Preview3D::draw_mesh(
  const std::vector<Vector > & vertices,
  const std::vector<std::vector<size_t> > & faces,
  const std::vector<Vector > & vertex_normals, 
  const std::vector<Vector > & vertex_colors, 
  const std::vector<Vector > & face_normals,
  const std::vector<Vector > & face_colors, 
  const bool show_lines,
  const float line_color[4],
  const bool show_faces,
  int faces_display_list)
{

  glEnable(GL_LIGHTING);
  if(show_faces)
  {
    int shader_id;
    glGetIntegerv(GL_CURRENT_PROGRAM,&shader_id);

	if (vertex_colors.size() > 0 || face_colors.size() > 0)
		glUseProgram(0);
		
    glEnable(GL_BLEND);
    glEnable(GL_POLYGON_OFFSET_FILL); // Avoid Stitching!
    glPolygonOffset(1.0, 1.0);
    //draw mesh faces
    glCallList(faces_display_list);
    glDisable(GL_POLYGON_OFFSET_FILL);
	
	if (vertex_colors.size() > 0 || face_colors.size() > 0)
		glUseProgram(shader_id);

  }

  if(show_lines)
  {
#ifndef PREVIEW3D_NO_SHADERS
    // push current shader
    int shader_id;
    glGetIntegerv(GL_CURRENT_PROGRAM,&shader_id);
#endif
    if(show_faces)
    {
#ifndef PREVIEW3D_NO_SHADERS
      // turn off shader
      glUseProgram(0);
#endif
      glDisable(GL_LIGHTING);
      glColor3fv(line_color);
    }
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glCallList(faces_display_list);
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#ifndef PREVIEW3D_NO_SHADERS
    // pop current shader
    glUseProgram(shader_id);
#endif
  }

}

void Preview3D::draw_floor()
{
#ifndef PREVIEW3D_NO_SHADERS
  // push shader
  int shader_id;
  glGetIntegerv(GL_CURRENT_PROGRAM,&shader_id);
  glUseProgram(0);
#endif
  glDisable(GL_LIGHTING);
  glColorMaterial ( GL_FRONT, GL_EMISSION ) ;
  glEnable(GL_COLOR_MATERIAL);
  glColorMaterial ( GL_FRONT, GL_AMBIENT_AND_DIFFUSE ) ;
  // Set material
  float black[] = {0.,0.,0.,1.};
  float grey[] = {0.5,0.5,0.5,1.};
  float white[] = {1.0,1.0,1.0,1.};
  glMaterialfv(GL_FRONT, GL_AMBIENT, black);
  glMaterialfv(GL_FRONT, GL_DIFFUSE, black);
  glMaterialfv(GL_FRONT, GL_SPECULAR, black);
  glMaterialfv(GL_FRONT, GL_EMISSION, black);
  glMaterialf(GL_FRONT, GL_SHININESS,0);
  glEnable(GL_LIGHTING);

  int GridSizeX = 10;
  int GridSizeY = 10;
  float SizeX = 0.5f;
  float SizeY = 0.5f;
  float depth = -1.5f;


  glBegin(GL_QUADS);
  glNormal3f(0,1,0);
  for (int x =-GridSizeX/2;x<GridSizeX/2;++x)
  for (int y =-GridSizeY/2;y<GridSizeY/2;++y)
  {
    if ((x+y)&0x00000001) //modulo 2
      glColor4fv(grey);
    else
      glColor4fv(white);
    glVertex3f(    x*SizeX,depth,    y*SizeY);
    glVertex3f((x+1)*SizeX,depth,    y*SizeY);
    glVertex3f((x+1)*SizeX,depth,(y+1)*SizeY);
    glVertex3f(    x*SizeX,depth,(y+1)*SizeY);
  }
  glEnd();


  depth=-GridSizeY/2*SizeY;
  glBegin(GL_QUADS);
  glNormal3f(0,0,1);
  for (int x =-GridSizeX/2;x<GridSizeX/2;++x)
  for (int y =-GridSizeY/2;y<GridSizeY/2;++y)
  {
    if ((x+y)&0x00000001) //modulo 2
      glColor4fv(white);
    else
      glColor4fv(grey);
    glVertex3f(    x*SizeX,    y*SizeY,depth);
    glVertex3f((x+1)*SizeX,    y*SizeY,depth);
    glVertex3f((x+1)*SizeX,(y+1)*SizeY,depth);
    glVertex3f(    x*SizeX,(y+1)*SizeY,depth);
  }
  glEnd();

  depth=-GridSizeX/2*SizeX;
  glBegin(GL_QUADS);
  glNormal3f(1,0,0);
  for (int x =-GridSizeX/2;x<GridSizeX/2;++x)
  for (int y =-GridSizeY/2;y<GridSizeY/2;++y)
  {
    if ((x+y)&0x00000001) //modulo 2
      glColor4fv(white);
    else
      glColor4fv(grey);
    glVertex3f(depth,    y*SizeY,    x*SizeX);
    glVertex3f(depth,    y*SizeY,(x+1)*SizeX);
    glVertex3f(depth,(y+1)*SizeY,(x+1)*SizeX);
    glVertex3f(depth,(y+1)*SizeY,    x*SizeX);
  }
  glEnd();

  // pop current shader
  glDisable(GL_COLOR_MATERIAL);
#ifndef PREVIEW3D_NO_SHADERS
  glUseProgram(shader_id);
#endif
}

void Preview3D::compile_mesh(
   const std::vector<Vector > & vertices, 
   const std::vector<std::vector<size_t> > & faces,
  const std::vector<Vector > & vertex_normals, 
  const std::vector<Vector > & vertex_colors, 
  const std::vector<Vector > & face_normals,
  const std::vector<Vector > & face_colors, 
   const bool flat_shading,
   const bool invert_normals,
   int & faces_display_list)
{
  if(Preview3D::faces_are_triangles(faces))
  {
    Preview3D::compile_triangle_mesh(
      vertices,
      faces,
      vertex_normals,
	  vertex_colors,
      face_normals,
	  face_colors,
      flat_shading,
      invert_normals,
      faces_display_list);
  }else if(Preview3D::faces_are_quads(faces))
  {
    Preview3D::compile_quad_mesh(
      vertices,
      faces,
      vertex_normals,
	  vertex_colors,
      face_normals,
	  face_colors,
      flat_shading,
      invert_normals,
      faces_display_list);
  }else
  {
    Preview3D::compile_polygon_mesh(
      vertices,
      faces,
      vertex_normals,
	  vertex_colors,
      face_normals,
	  face_colors,
      flat_shading,
      invert_normals,
      faces_display_list);
  }
}

void Preview3D::compile_triangle_mesh(
   const std::vector<Vector > & vertices, 
   const std::vector<std::vector<size_t> > & faces,
  const std::vector<Vector > & vertex_normals, 
  const std::vector<Vector > & vertex_colors, 
  const std::vector<Vector > & face_normals,
  const std::vector<Vector > & face_colors, 
   const bool flat_shading,
   const bool invert_normals,
   int & faces_display_list)
{
  // Delete old display list
  glDeleteLists(faces_display_list, 1);
  // generate new display list
  faces_display_list = glGenLists(1);

  glNewList(faces_display_list, GL_COMPILE);
  glBegin(GL_TRIANGLES);
  // loop over all faces
  int face_index = 0;
  for(face_index = 0; face_index<faces.size();face_index++)
  {
  
	if (face_colors.size() > 0)
	{
	  glColor3d(face_colors[face_index][0], face_colors[face_index][1], face_colors[face_index][2]);
	}
	
    // flat shading
    if(flat_shading)
    {
      
      glNormal3f(
        (invert_normals ? -1 : 1) *face_normals[face_index][0],
        (invert_normals ? -1 : 1) *face_normals[face_index][1],
        (invert_normals ? -1 : 1) *face_normals[face_index][2]
        );
    }
    {
      // loop over vertices in this face
      for(std::vector<size_t>::const_iterator vit = faces[face_index].begin();
          vit != faces[face_index].end();
          vit++)
      {
	  
	  	if (vertex_colors.size() > 0)
  	    {
	      glColor3d(vertex_colors[*vit][0], vertex_colors[*vit][1], vertex_colors[*vit][2]);
		}

        // not flat shading goes here
        if(!flat_shading)
        {
          glNormal3f(
            (invert_normals ? -1 : 1) *vertex_normals[*vit][0],
            (invert_normals ? -1 : 1) *vertex_normals[*vit][1],
            (invert_normals ? -1 : 1) *vertex_normals[*vit][2]
            );
        }
        // assumes every point in vertices is length == 3
        glVertex3f(
          vertices[(*vit)][0],
          vertices[(*vit)][1],
          vertices[(*vit)][2]);

      }
    }
  }
  glEnd();
  glEndList();
}

void Preview3D::compile_quad_mesh(
   const std::vector<Vector > & vertices, 
   const std::vector<std::vector<size_t> > & faces,
   const std::vector<Vector > & vertex_normals, 
   const std::vector<Vector > & vertex_colors, 
   const std::vector<Vector > & face_normals,
   const std::vector<Vector > & face_colors, 
   const bool flat_shading,
   const bool invert_normals,
   int & faces_display_list)
{
  // Delete old display list
  glDeleteLists(faces_display_list, 1);
  // generate new display list
  faces_display_list = glGenLists(1);

  glNewList(faces_display_list, GL_COMPILE);
  glBegin(GL_QUADS);
  // loop over all faces
  int face_index = 0;
  for(face_index = 0; face_index<faces.size();face_index++)
  {
    // flat shading
    if(flat_shading)
    {
      
      glNormal3f(
        (invert_normals ? -1 : 1) *face_normals[face_index][0],
        (invert_normals ? -1 : 1) *face_normals[face_index][1],
        (invert_normals ? -1 : 1) *face_normals[face_index][2]
        );
    }
    {
      // loop over vertices in this face
      for(std::vector<size_t>::const_iterator vit = faces[face_index].begin();
          vit != faces[face_index].end();
          vit++)
      {
        // not flat shading goes here
        if(!flat_shading)
        {
          glNormal3f(
            (invert_normals ? -1 : 1) *vertex_normals[*vit][0],
            (invert_normals ? -1 : 1) *vertex_normals[*vit][1],
            (invert_normals ? -1 : 1) *vertex_normals[*vit][2]
            );
        }
        // assumes every point in vertices is length == 3
        glVertex3f(
          vertices[(*vit)][0],
          vertices[(*vit)][1],
          vertices[(*vit)][2]);

      }
    }
  }
  glEnd();
  glEndList();
}

void Preview3D::compile_polygon_mesh(
   const std::vector<Vector > & vertices, 
   const std::vector<std::vector<size_t> > & faces,
   const std::vector<Vector > & vertex_normals, 
   const std::vector<Vector > & vertex_colors, 
   const std::vector<Vector > & face_normals,
   const std::vector<Vector > & face_colors, 
   const bool flat_shading,
   const bool invert_normals,
   int & faces_display_list)
{
  // Delete old display list
  glDeleteLists(faces_display_list, 1);
  // generate new display list
  faces_display_list = glGenLists(1);

  glNewList(faces_display_list, GL_COMPILE);
  // loop over all faces
  int face_index = 0;
  for(face_index = 0; face_index<faces.size();face_index++)
  {
    // flat shading
    if(flat_shading)
    {
      
      glNormal3f(
        (invert_normals ? -1 : 1) *face_normals[face_index][0],
        (invert_normals ? -1 : 1) *face_normals[face_index][1],
        (invert_normals ? -1 : 1) *face_normals[face_index][2]
        );
    }
    {
      glBegin(GL_POLYGON);
      // loop over vertices in this face
      for(std::vector<size_t>::const_iterator vit = faces[face_index].begin();
          vit != faces[face_index].end();
          vit++)
      {
        // not flat shading goes here
        if(!flat_shading)
        {
          glNormal3f(
            (invert_normals ? -1 : 1) *vertex_normals[*vit][0],
            (invert_normals ? -1 : 1) *vertex_normals[*vit][1],
            (invert_normals ? -1 : 1) *vertex_normals[*vit][2]
            );
        }
        // assumes every point in vertices is length == 3
        glVertex3f(
          vertices[(*vit)][0],
          vertices[(*vit)][1],
          vertices[(*vit)][2]);

      }
    glEnd();
    }
  }
  glEndList();
}

void Preview3D::compute_face_normals(
  const std::vector<Vector > & vertices, 
  const std::vector<std::vector<size_t> > & faces,
  std::vector<Vector > & face_normals)
{
  face_normals.resize(faces.size());
  int face_index = 0;
  // loop over faces
  for(std::vector<std::vector<size_t> >::const_iterator fit = faces.begin();
      fit != faces.end();
      fit++,face_index++)
  {
    // Only valid for triangle meshes !!!
    Vector v1;
    v1.resize(3);
    v1[0] = vertices[(*fit)[1]][0] - vertices[(*fit)[0]][0];
    v1[1] = vertices[(*fit)[1]][1] - vertices[(*fit)[0]][1];
    v1[2] = vertices[(*fit)[1]][2] - vertices[(*fit)[0]][2];
    Vector v2;
    v2.resize(3);
    v2[0] = vertices[(*fit)[2]][0] - vertices[(*fit)[0]][0];
    v2[1] = vertices[(*fit)[2]][1] - vertices[(*fit)[0]][1];
    v2[2] = vertices[(*fit)[2]][2] - vertices[(*fit)[0]][2];
    Vector v1_cross_v2;
    v1_cross_v2.resize(3);
    v1_cross_v2[0] = v1[1]*v2[2] - v1[2]*v2[1];
    v1_cross_v2[1] = -(v1[0]*v2[2] - v1[2]*v2[0]);
    v1_cross_v2[2] = v1[0]*v2[1] - v1[1]*v2[0];
    double length = sqrt(
      v1_cross_v2[0]*v1_cross_v2[0] +
      v1_cross_v2[1]*v1_cross_v2[1] +
      v1_cross_v2[2]*v1_cross_v2[2]);
    v1_cross_v2[0] /= length;
    v1_cross_v2[1] /=  length;
    v1_cross_v2[2] /=  length;
    face_normals[face_index] = v1_cross_v2;
  }
}

void Preview3D::compute_vertex_normals(
  const std::vector<Vector > & vertices, 
  const std::vector<std::vector<size_t> > & faces,
  std::vector<Vector > & vertex_normals)
{  
  std::vector<Vector > face_normals;
  compute_face_normals(
    vertices,
    faces,
    face_normals);
  vertex_normals.resize(vertices.size());
  for(std::vector<Vector >::iterator vnit = vertex_normals.begin();
      vnit != vertex_normals.end();
      vnit++)
  {
    Vector normal;
    normal.resize(3);
    (*vnit) = normal;
  }

  int face_index = 0;
  // loop over faces
  for(std::vector<std::vector<size_t> >::const_iterator fit = faces.begin();
      fit != faces.end();
      fit++,face_index++)
  {
    // loop over vertices in this face
    for(std::vector<size_t>::const_iterator vit = (*fit).begin();
        vit != (*fit).end();
        vit++)
    {
      vertex_normals[*vit][0] += face_normals[face_index][0];
      vertex_normals[*vit][1] += face_normals[face_index][1];
      vertex_normals[*vit][2] += face_normals[face_index][2];
    }
  }

  // normalize
  for(int i=0; i<vertex_normals.size(); i++)
  {
    double length = sqrt(
      vertex_normals[i][0]*vertex_normals[i][0] +
      vertex_normals[i][1]*vertex_normals[i][1] +
      vertex_normals[i][2]*vertex_normals[i][2]);
    vertex_normals[i][0] /= length;
    vertex_normals[i][1] /= length;
    vertex_normals[i][2] /= length;
  }
}

void Preview3D::compute_bounding_box_and_centroid(
  const std::vector<Vector > & vertices, 
  Vector & min_point,
  Vector & max_point,
  Vector & centroid)
{

  centroid.resize(3);
  min_point.resize(3,std::numeric_limits<double>::max());
  max_point.resize(3,-std::numeric_limits<double>::max());
  for(std::vector<Vector >::const_iterator vit = vertices.begin();
      vit != vertices.end();
      vit++)
  {
    for(int i=0;i<3;i++)
    {
      centroid[i] += (*vit)[i];
      if(min_point[i] > (*vit)[i])
      {
        min_point[i] = (*vit)[i];
      }
      if(max_point[i] < (*vit)[i])
      {
        max_point[i] = (*vit)[i];
      }
    }
  }
  centroid[0] /= vertices.size();
  centroid[1] /= vertices.size();
  centroid[2] /= vertices.size();
}

void Preview3D::get_scale_and_shift_to_fit_mesh( 
  const std::vector<Vector > & vertices, 
  float& zoom,
  float shift[])
{
  //Compute mesh centroid
  Vector centroid;
  Vector min_point;
  Vector max_point;
  Preview3D::compute_bounding_box_and_centroid(
      vertices,
      min_point,
      max_point,
      centroid);

  shift[0] = -centroid[0];
  shift[1] = -centroid[1];
  shift[2] = -centroid[2];
  double x_scale = fabs(max_point[0] - min_point[0]);
  double y_scale = fabs(max_point[1] - min_point[1]);
  double z_scale = fabs(max_point[2] - min_point[2]);
  zoom = 2.0/ std::max(z_scale,std::max(x_scale,y_scale));
}

bool Preview3D::test_for_inverted_normals(
  const std::vector<Vector > & vertices, 
  const std::vector<Vector > & vertex_normals)
{
  Vector centroid;
  Vector min_point;
  Vector max_point;
  Preview3D::compute_bounding_box_and_centroid(
      vertices,
      min_point,
      max_point,
      centroid);

  double average_dot_product = 0.0;
  // loop over vertices
  int i = 0;
  for(std::vector<Vector >::const_iterator vit = vertices.begin();
      vit != vertices.end();
      vit++,i++)
  {
    // take dot product of unit displacement vector and normal
    Vector unit_displacement = (*vit);
    unit_displacement[0] -= centroid[0];
    unit_displacement[1] -= centroid[1];
    unit_displacement[2] -= centroid[2];
    double length = sqrt(
        unit_displacement[0]*unit_displacement[0] +
        unit_displacement[1]*unit_displacement[1] +
        unit_displacement[2]*unit_displacement[2]);
    unit_displacement[0] /= length;
    unit_displacement[1] /= length;
    unit_displacement[2] /= length;
    Vector unit_normal = vertex_normals[i];
    length = sqrt(
        unit_normal[0]*unit_normal[0] +
        unit_normal[1]*unit_normal[1] +
        unit_normal[2]*unit_normal[2]);
    unit_normal[0] /= length;
    unit_normal[1] /= length;
    unit_normal[2] /= length;
    double dot_product = 
      unit_displacement[0]*unit_normal[0] +
      unit_displacement[1]*unit_normal[1] +
      unit_displacement[2]*unit_normal[2];
    average_dot_product += dot_product;
  }
  return average_dot_product < 0;
}

bool Preview3D::faces_are_triangles(
  const std::vector<std::vector<size_t> > & faces)
{
  // loop over faces
  for(std::vector<std::vector<size_t> >::const_iterator fit = faces.begin();
      fit != faces.end();
      fit++)
  {
    // if face is not triangle then can't say all are
    if((*fit).size() != 3) return false;
  }
  // made it through, so all faces are triangles
  return true;
}


bool Preview3D::faces_are_quads(
  const std::vector<std::vector<size_t> > & faces)
{
  // loop over faces
  for(std::vector<std::vector<size_t> >::const_iterator fit = faces.begin();
      fit != faces.end();
      fit++)
  {
    // if face is not quad then can't say all are
    if((*fit).size() != 4) return false;
  }
  // made it through, so all faces are quads
  return true;
}

// Routine to set a quaternion from a rotation axis and angle
// ( input axis = float[3] angle = float  output: quat = float[4] )
void Preview3D::SetQuaternionFromAxisAngle(const float *axis, float angle, float *quat)
{
  float sina2, norm;
  sina2 = (float)sin(0.5f * angle);
  norm = (float)sqrt(axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2]);
  quat[0] = sina2 * axis[0] / norm;
  quat[1] = sina2 * axis[1] / norm;
  quat[2] = sina2 * axis[2] / norm;
  quat[3] = (float)cos(0.5f * angle);
  
}

// Routine to convert a quaternion to a 4x4 matrix
// ( input: quat = float[4]  output: mat = float[4*4] )
void Preview3D::ConvertQuaternionToMatrix(const float *quat, float *mat)
{
  float yy2 = 2.0f * quat[1] * quat[1];
  float xy2 = 2.0f * quat[0] * quat[1];
  float xz2 = 2.0f * quat[0] * quat[2];
  float yz2 = 2.0f * quat[1] * quat[2];
  float zz2 = 2.0f * quat[2] * quat[2];
  float wz2 = 2.0f * quat[3] * quat[2];
  float wy2 = 2.0f * quat[3] * quat[1];
  float wx2 = 2.0f * quat[3] * quat[0];
  float xx2 = 2.0f * quat[0] * quat[0];
  mat[0*4+0] = - yy2 - zz2 + 1.0f;
  mat[0*4+1] = xy2 + wz2;
  mat[0*4+2] = xz2 - wy2;
  mat[0*4+3] = 0;
  mat[1*4+0] = xy2 - wz2;
  mat[1*4+1] = - xx2 - zz2 + 1.0f;
  mat[1*4+2] = yz2 + wx2;
  mat[1*4+3] = 0;
  mat[2*4+0] = xz2 + wy2;
  mat[2*4+1] = yz2 - wx2;
  mat[2*4+2] = - xx2 - yy2 + 1.0f;
  mat[2*4+3] = 0;
  mat[3*4+0] = mat[3*4+1] = mat[3*4+2] = 0;
  mat[3*4+3] = 1;
}

// Routine to multiply 2 quaternions (ie, compose rotations)
// ( input q1 = float[4] q2 = float[4]  output: qout = float[4] )
void Preview3D::MultiplyQuaternions(const float *q1, const float *q2, float *qout)
{
  float qr[4];
	qr[0] = q1[3]*q2[0] + q1[0]*q2[3] + q1[1]*q2[2] - q1[2]*q2[1];
	qr[1] = q1[3]*q2[1] + q1[1]*q2[3] + q1[2]*q2[0] - q1[0]*q2[2];
	qr[2] = q1[3]*q2[2] + q1[2]*q2[3] + q1[0]*q2[1] - q1[1]*q2[0];
	qr[3]  = q1[3]*q2[3] - (q1[0]*q2[0] + q1[1]*q2[1] + q1[2]*q2[2]);
  qout[0] = qr[0]; qout[1] = qr[1]; qout[2] = qr[2]; qout[3] = qr[3];
}


//  Callback function called when the 'AutoRotate' variable value of the tweak bar has changed
void TW_CALL Preview3D::SetAutoRotateCB(const void *value, void *clientData)
{
  static_cast<Preview3D *>(clientData)->SetAutoRotate(*static_cast<const int *>(value));
}
void TW_CALL Preview3D::GetAutoRotateCB(void *value, void *clientData)
{
  *static_cast<int *>(value) = static_cast<Preview3D*>(clientData)->GetAutoRotate();
}
void TW_CALL Preview3D::view_xy_planeCB(void *clientData)
{
  static_cast<Preview3D *>(clientData)->view_xy_plane();
}
void TW_CALL Preview3D::view_xz_planeCB(void *clientData)
{
  static_cast<Preview3D *>(clientData)->view_xz_plane();
}
void TW_CALL Preview3D::view_yz_planeCB(void *clientData)
{
  static_cast<Preview3D *>(clientData)->view_yz_plane();
}
void TW_CALL Preview3D::snap_to_canonical_quaternionCB(void *clientData)
{
  Preview3D::snap_to_canonical_quaternion(
    static_cast<Preview3D *>(clientData)->g_Rotation);
}
